#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <sys/epoll.h>
#include <memory>
#include <cstring>
#include "err.hpp"
#include "log.hpp"
#include "sock.hpp"
namespace epoll_ns
{
    static const uint16_t defaultPort = 8080;
    static const int size = 128;//size不小于0就行 
    static const int defaultValue = -1;
    static const int defaultNum = 64;//申请就绪事件的空间
    static const int timeOut = 1000;//超时时间，单位毫秒
    static const int readBuffer = 1024*8;
    using func_t = std::function<std::string(const std::string&)>;
    class EpollServer
    {
    public:
        EpollServer(func_t func, uint16_t port = defaultPort)
            :_port(port)
            ,_listenSocket(defaultValue)
            ,_epFd(defaultValue)
            ,_rEvents(nullptr)
            ,_num(defaultNum)
            ,_func(func)
        {}
        ~EpollServer()
        {
            if(_listenSocket >= 0) close(_listenSocket);
            if(_epFd >= 0)         close(_epFd);
            if(_rEvents)           delete[] _rEvents;
        }
    public:
        void InitServer();
        void Start();
        void HandlerEvent(int readyNum);
    private:
        uint16_t _port;
        int _listenSocket;
        int _epFd;//epoll模型
        struct epoll_event* _rEvents;//已经就绪的事件数组指针
        int _num;//每一次从就绪队列中拿走的事件数量
        func_t _func;//业务处理函数
    };

    void EpollServer::InitServer()
    {
        //1、创建socket
        _listenSocket = Sock::Socket();
        Sock::Bind(_listenSocket, _port);
        Sock::Listen(_listenSocket);
        //2、创建epoll模型
        _epFd = epoll_create(size);
        if(_epFd < 0)//创建epoll模型失败
        {
            LogMessage(FATAL, "epoll create error: %s", strerror(errno));
            exit(EPOLL_CREATE_ERR);
        }
        //3、将_listenSocket添加到epoll模型中
        struct epoll_event ev;
        ev.events = EPOLLIN;//关心读事件     ev.events = EPOLLIN | EPOLLET;//边缘触发，fd需非阻塞
        ev.data.fd = _listenSocket;//当事件就绪，被重新捞取上来，就知道是哪一个fd就绪了
        epoll_ctl(_epFd, EPOLL_CTL_ADD, _listenSocket, &ev);
        //4、申请就绪事件的空间
        _rEvents = new struct epoll_event[_num];
        LogMessage(NORMAL, "Init server success");
    }
    void EpollServer::Start()
    {
        while(1)
        {
            int readyNum = epoll_wait(_epFd, _rEvents, _num, timeOut);
            switch(readyNum)
            {
            case 0://超时
                LogMessage(NORMAL, "Timeout");
                break;
            case -1:
                LogMessage(WARNING, "Epoll_wait failed, code: %d, errstring: %s", errno, strerror(errno));
                break;
            default:
                LogMessage(NORMAL, "Have event ready");
                HandlerEvent(readyNum);//n是就绪事件的数量
                break;
            }
        }
    }
    void EpollServer::HandlerEvent(int readyNum)
    {
        for(int i = 0; i<readyNum; ++i)
        {
            uint32_t events = _rEvents[i].events;//哪一个事件就绪了
            int sock = _rEvents[i].data.fd;//拿到这个事件的fd
            //处理这个fd的事件
            if(sock == _listenSocket && (events & EPOLLIN))//_listenSocket的读事件就绪,获取新链接
            {
                std::string clientIp;
                uint16_t clientPort;
                int fd = Sock::Accept(_listenSocket, &clientIp, &clientPort);
                if(fd < 0)
                {
                    LogMessage(WARNING, "Accept error");
                    continue;
                }
                //将读取的fd和事件加入epoll模型中
                struct epoll_event ev;
                ev.events = EPOLLIN;
                ev.data.fd = fd;
                epoll_ctl(_epFd, EPOLL_CTL_ADD, fd, &ev);
            }
            else if(events & EPOLLIN)//普通的读事件就绪
            {
                //这里读取依旧有问题
                char buffer[readBuffer];//因为没有自定义协议保证数据是否一次被读完，临时变量。下一次读取时上一次写入的数据已被释放
                int n = recv(sock, buffer, sizeof(buffer)-1, 0);
                if(n > 0)
                {
                    buffer[n] = 0;
                    LogMessage(DEBUG, "Client# %s", buffer);
                    //处理数据
                    std::string response = _func(buffer);
                    send(sock, response.c_str(), response.size(), 0);
                }
                else if(n == 0)//对端关闭连接
                {
                    //在epoll中对该fd的该事件取消关心,先在epoll中移除，再关闭fd
                    epoll_ctl(_epFd, EPOLL_CTL_DEL, sock, nullptr);
                    close(sock);
                    LogMessage(DEBUG, "Client quit");
                }
                else//读取出错
                {
                    epoll_ctl(_epFd, EPOLL_CTL_DEL, sock, nullptr);
                    close(sock);
                    LogMessage(ERROR, "recv error, code: %d, errstring: %s", errno, strerror(errno));
                }
            }
            else//写
            {}
        }
    }
}
